"""
Copyright (c) 2022 Huawei Technologies Co.,Ltd.

openGauss is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:

          http://license.coscl.org.cn/MulanPSL2

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
"""
"""
Case Type   : 端口故障类
Case Name   : 备节点重建时，备节点TCP端口占用
Create At   : 2021/07/13
Owner       : @wan005
Description :
    1.备节点注入TCP端口被占用故障
    ./cfe "inject rPort_conflict (port) values({port})"
    2.备节点重建
    gs_ctl build -D {dn1} -b full
    3.检查集群状态
    4.清除故障 恢复环境 再次重建
    ./cfe "clean rPort_conflict where port ={port}"
    gs_ctl start -D {dn1} -M standby
    gs_om -t restart
    gs_om -t status --detail
    5.运行TPCC
    6.检查数据一致性
    7.恢复环境
Expect      :
    1.故障注入成功
    2.备机重建失败
    3.数据库状态异常
    4.清除故障 恢复环境 再次重建成功
    5.运行成功
    6.数据一致
    7.恢复环境
History     :
    Modified by @wan005 2021/8/4 : 优化故障清理断言和teardown
    Modified by @wan005 2021/8/30 : 优化备机build等待时间并优化格式
"""
import time
import unittest

from testcase.utils.ComThread import ComThread
from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.CommonSH import RestartDbCluster
from testcase.utils.Constant import Constant
from testcase.utils.Logger import Logger
from yat.test import Node, macro


class ReliabilityPort(unittest.TestCase):
    nodes = ("PrimaryDbUser", "Standby1DbUser", "Standby2DbUser")

    @RestartDbCluster(*nodes)
    def setUp(self):
        self.log = Logger()
        self.primary_sh = CommonSH(self.nodes[0])
        self.primary_node = Node(self.nodes[0])

        self.constant = Constant()
        self.com = Common()
        self.primary_root_node = Node(node="PrimaryRoot")
        self.standby1_root_node = Node(node="Standby1Root")
        self.standby1_sh = CommonSH(self.nodes[1])
        self.log.info("setUp-实例化:ok")

        self.log.info("Opengauss_Reliability_Port_Case010:开始执行")
        self.log.info("停止备节点")
        result = self.standby1_sh.executeGsctl("stop", "server stopped")
        self.assertTrue(result)

        self.port = Node("PrimaryDbUser").db_port
        self.inject_cmd = f"rPort_conflict (port) values({self.port})"
        self.clean_cmd = f"rPort_conflict where port ={self.port}"

    def test_main(self):
        try:
            text = "--step:1:备节点注入TCP端口被占用故障;expect:成功"
            self.log.info(text)
            self.log.info("查看当前端口未被占用")
            shell_cmd = f"netstat -anp|grep {self.port}"
            self.log.info(shell_cmd)
            result = self.standby1_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertNotIn("gaussdb", result, "执行失败" + text)

            self.log.info("注入故障")
            session2 = ComThread(self.com.cfe_inject,
                                 args=(self.standby1_root_node,
                                       self.inject_cmd))
            session2.setDaemon(True)
            session2.start()
            time.sleep(2)

            text = "--step:2:build备节点;expect:失败"
            self.log.info(text)
            sql = f"source {macro.DB_ENV_PATH};" \
                  f"gs_ctl build -D {macro.DB_INSTANCE_PATH} -b full"
            session1 = ComThread(self.com.get_sh_result,
                                 args=(Node("Standby1DbUser"), sql))
            session1.setDaemon(True)
            session1.start()

            self.log.info("注入故障result")
            session2.join(30)
            result = session2.get_result()
            self.log.info(result)

            self.log.info("build备机result")
            session1.join(60*30)
            build_result = session1.get_result()
            self.log.info("build_result:")
            self.log.info(build_result)
            self.assertIn(f"Port {self.port} is used", build_result,
                          "执行失败" + text)

            self.log.info("注入故障后查看当前端口被占用")
            result = self.standby1_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertIn(self.port, result, "执行失败" + text)
            self.assertIn("0.0.0.0", result, "执行失败" + text)
            self.assertIn("/python", result, "执行失败" + text)
            self.assertNotIn("gaussdb", result, "执行失败" + text)

            text = "--step:3:检查集群状态;expect:异常"
            self.log.info(text)
            db_status = self.primary_sh.getDbClusterStatus("detail")
            self.log.info(db_status)
            self.assertTrue("Degraded" in db_status, "执行失败" + text)
            self.assertTrue("S Down" in db_status, "执行失败" + text)
        finally:
            text = "--step:4:清除故障 恢复环境 再次build;expect:成功"
            self.log.info(text)
            result = self.com.cfe_clean(self.standby1_root_node,
                                        self.clean_cmd)
            self.log.info(result)
            self.assertIn("clean rPortConflict successfully", result,
                          "执行失败" + text)

            self.log.info("查看当前端口未被占用")
            shell_cmd = f"netstat -anp|grep {self.port}"
            self.log.info(shell_cmd)
            result = self.standby1_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertEqual("", result.strip(), "执行失败" + text)

        self.log.info("再次build备节点;expect:成功")
        result = self.standby1_sh.executeGsctl("build",
                                               "server started",
                                               param="-b full")
        self.assertTrue(result, "执行失败" + text)

        db_status = self.primary_sh.getDbClusterStatus()
        self.log.info(db_status)
        self.assertTrue("Degraded" in db_status or "Normal" in db_status,
                        "执行失败" + text)

        text = "--step:5:运行TPCC;expect:成功"
        self.log.info(text)
        result = self.com.startTPCC(self.primary_root_node, macro.TPCC_PATH)
        self.log.info(result)
        self.assertIn(self.constant.TPCC_SUCCESS_MSG, result, "执行失败" + text)

        self.log.info("检查主备是否同步")
        time.sleep(10)
        self.log.info("获取节点数量")
        node_num = self.primary_sh.get_node_num()
        res = self.primary_sh.check_location_consistency("primary", node_num)
        self.assertTrue(res, "执行失败" + text)

        text = "--step:6:检查数据一致性;expect:成功"
        self.log.info(text)
        nodes = (self.primary_node, Node(self.nodes[1]), Node(self.nodes[2]))
        flag = self.com.check_data_sample_by_all(r'\d', *nodes)
        self.assertTrue(flag, "执行失败" + text)
        table_dict = self.com.format_sql_result(
            self.primary_sh.executDbSql(r'\d'))
        self.log.info(table_dict)
        table_name = table_dict.get('Name')
        self.log.info(table_name)
        for name in table_name:
            select_sql = f"select count(*) from {name};"
            flag = self.com.check_data_sample_by_all(select_sql,
                                                     *nodes)
            self.assertTrue(flag, "执行失败" + text)

    def tearDown(self):
        text = "--step7:环境清理;expect:成功;expect:成功"
        self.log.info(text)
        self.com.cfe_clean(self.standby1_root_node, self.clean_cmd)

        self.log.info("build备节点")
        result = self.standby1_sh.executeGsctl("build",
                                               "server started",
                                               param="-b full")
        self.log.info(result)

        db_status = self.primary_sh.getDbClusterStatus("detail")
        self.log.info(db_status)
        self.assertTrue("Degraded" in db_status or "Normal" in db_status,
                        "执行失败" + text)
        self.log.info("Opengauss_Reliability_Port_Case010:执行完成")
